package com.googlecode.flex_solder.controls  {
  /**
   * $Id: TimeCombo.as 29 2009-05-30 13:12:19Z fuller.sethm $
   * 
   * Author: Seth M. Fuller
   * Copyright: 2008
   *
   *  This class was inspired by the TimeInput class in the Yahoo
   *  Astra Flex components library. This class, of course uses
   *  ComboBox controls, as opposed to the UITextField controls used
   *  in the TimeInput control.
   *  
   *  One important way that this class differs from the TimeInput
   *  class is that the date/time value returned can be null, if the
   *  user doesn't choose values for the ComboBox controls. I use the
   *  return of a null Date to indicate that the user didn't make a
   *  choice in the control. When the user does not make a choice in
   *  the TimeCombo control the timeValue = null and hour, minute, and
   *  second are set to -1.
   *
   * For easier input, when the user chooses an hour, the minute
   * ComboBox changes to 00 (Zero minutes) and the AM/PM dropdown
   * changes to AM. To change the AM/PM dropdown to default to PM when
   * the user selects an hour change the style 'defaultMorning' to
   * false.
   *
   *  When you retrieve the timeValue (Date) property, the time is
   *  calculated and the hour, minute, and second properties are also
   *  set. When the timeValue Date object is created, the date portion
   *  is today's date, but the date portion of the Date object is
   *  ignored by this control.
   *
   *  Permission is hereby granted, free of charge, to any person
   *  obtaining a copy of this software and associated documentation
   *  files (the "Software"), to deal in the Software without
   *  restriction, including without limitation the rights to use,
   *  copy, modify, merge, publish, distribute, sublicense, and/or
   *  sell copies of the Software, and to permit persons to whom the
   *  Software is furnished to do so, subject to the following
   *  conditions:
   *
   *  The above copyright notice and this permission notice shall be
   *  included in all copies or substantial portions of the Software.
   *
   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   *  OTHER DEALINGS IN THE SOFTWARE.
   *
   * This file is licensed under the BSD (revised) open source
   * license.
   *
   * This is part of the flex-solder components hosted at
   * flex-solder.googlecode.com.
   *
   *======================================================================
   *
   * Contains third-party source code released with the same license terms
   * that appear above with the following copyright notice:
   *
   * Copyright (c) 2008 Yahoo! Inc.  All rights reserved.  The copyrights
   * embodied in the content of this file are licensed under the BSD
   * (revised) open source license
   *
   */
  import com.googlecode.flex_solder.controls.BaseMultiCombo;
	
  import flash.events.Event;
  import flash.events.FocusEvent;
  import flash.events.KeyboardEvent;
  import flash.ui.Keyboard;
  import flash.text.TextLineMetrics;
	
  import mx.collections.ArrayCollection;

  import mx.core.EdgeMetrics;
  import mx.core.IBorder;
  import mx.core.UITextField;
  import mx.containers.HBox;
  import mx.controls.Text;
  import mx.controls.ComboBox;

  import mx.events.ListEvent;
  import mx.events.FlexEvent;

  import mx.styles.CSSStyleDeclaration;
  import mx.styles.StyleManager;
	
  //--------------------------------------
  //  Events
  //--------------------------------------
	
  /**
   *  Dispatched when text in the TimeCombo control changes
   *  through user input.
   *  This event does not occur if you use data binding or 
   *  ActionScript code to change the text.
   *
   *  <p>Even though the default value of the <code>Event.bubbles</code> property 
   *  is <code>true</code>, this control dispatches the event with 
   *  the <code>Event.bubbles</code> property set to <code>false</code>.</p>
   *
   *  @eventType flash.events.Event.CHANGE
   */
  [Event(name="change", type="flash.events.Event")]
		
    //--------------------------------------
    //  Styles
    //--------------------------------------

    /**
     * If true, the seconds value will be displayed.
     * 
     * @default true
     */
    [Style(name="showSeconds", type="Boolean", inherit="no")]

    /**
     * The number of hours between each value in the hours dropdown
     * 
     * @default 1
     */
    [Style(name="hoursIncrement", type="Number", inherit="no")]

    /**
     * The number of minutes between each value in the minutes dropdown
     * 
     * @default 15
     */
    [Style(name="minutesIncrement", type="Number", inherit="no")]

    /**
     * The number of seconds between each value in the seconds dropdown
     * 
     * @default 15
     */
    [Style(name="secondsIncrement", type="Number", inherit="no")]

    /**
     * If true, and the useTwelveHourFormat style is also true, the AM or PM
     * string will be displayed.
     * 
     * @default true
     */
    [Style(name="showAMPM", type="Boolean", inherit="no")]

    /**
     * If true, the hours display will accept values from 1-12 only. If false,
     * time will be displayed and edited in 24 hour format. In that case, the AM
     * and PM value will be ignored.
     * 
     * @default true
     */
    [Style(name="useTwelveHourFormat", type="Boolean", inherit="no")]

    /**
     * The String displayed for the hours toolTip.
     * 
     * @default Hours
     */
    [Style(name="hoursToolTip", type="String", inherit="no")]

    /**
     * The String displayed for the minutes toolTip.
     * 
     * @default Minutes
     */
    [Style(name="minutesToolTip", type="String", inherit="no")]

    /**
     * The String displayed for the seconds toolTip.
     * 
     * @default Seconds
     */
    [Style(name="secondsToolTip", type="String", inherit="no")]

    /**
     * If true, set the AM/PM dropdown to the AM string.
     * If false, set the AM/PM dropdown to the PM string.
     * 
     * @default true
     */
    [Style(name="defaultMorning", type="Boolean", inherit="no")]

    /**
     * The String displayed for the AM value.
     * 
     * @default AM
     */
    [Style(name="AMText", type="String", inherit="no")]

    /**
     * The String displayed for the PM value.
     * 
     * @default PM
     */
    [Style(name="PMText", type="String", inherit="no")]
	
    //--------------------------------------
    //  Other Metadata
    //--------------------------------------
	
    [DefaultBindingProperty(source="timeValue", destination="timeValue")]
	
    /**
     * An multi-ComboBox control for time values. 
     * Includes ComboBox controls for hours, minutes, and seconds.
     * 
     * @author Seth M. Fuller
     */
    public class TimeCombo extends BaseMultiCombo {
    private var layoutStyleChanged:Boolean = false;

    protected var hoursCombo:ComboBox = null;
    protected var minutesCombo:ComboBox = null;
    protected var secondsCombo:ComboBox = null;
    protected var AMPMCombo:ComboBox = null;


    protected var hoursList:ArrayCollection = null;
    protected var minutesList:ArrayCollection = null;
    protected var secondsList:ArrayCollection = null;
    protected var amPmList:ArrayCollection = null;
		
    protected var componentWidths:ArrayCollection = null;

    public static const TEXT_WIDTH_PADDING:Number = 5;
    private var timeComboWidth:Number = 0;
    private var timeComboHeight:Number = 0;
    private const minComboWidth:Number = 40;

    public static const timeComboWidthPad:Number = 6;
    public static const amPmGap:Number = 3;

    protected static const hoursPerDay:int = 24;
    protected static const hoursPerHalfDay:int = 12;
    protected static const minutesPerHour:int = 60;
    protected static const secondsPerMinute:int = 60;

    protected var hoursInc:int = 1;
    protected var minutesInc:int = 15;
    protected var secondsInc:int = 15;

    //--------------------------------------
    //  Static Methods
    //--------------------------------------
	
    /**
     * @private
     * Sets the default style values for this control.
     */
    public static function initializeStyles():void
    {
      var styleDeclaration:CSSStyleDeclaration
	= StyleManager.getStyleDeclaration("TimeCombo");
      if(!styleDeclaration)
	{
	  styleDeclaration = new CSSStyleDeclaration();
	}
			
      styleDeclaration.defaultFactory = function():void
      {
	this.useTwelveHourFormat = true;
	this.AMText = "AM";
	this.PMText = "PM";
	this.defaultMorning = true;
	this.showAMPM = true;
	this.hoursIncrement = 1;
	this.minutesIncrement = 15;
	this.secondsIncrement = 15;
	this.showSeconds = false;
	this.hoursToolTip = "Hours";
	this.minutesToolTip = "Minutes";
	this.secondsToolTip = "Seconds";
	this.paddingBottom = 0;
      };
			
      StyleManager.setStyleDeclaration("TimeCombo", styleDeclaration, false);
    }
    initializeStyles();
		
		
    //--------------------------------------
    //  Constructor
    //--------------------------------------
	
    /**
     * Constructor.
     */
    public function TimeCombo()
    {
      super();
      this.fieldCount = 4;

    }
		
    //--------------------------------------
    //  Properties
    //--------------------------------------

    /**
     * @private
     * Storage for the timeValue property.
     */
    protected var _timeValue:Date = null;
		
    protected var _hour:int = -1;
    protected var _minute:int = -1;
    protected var _second:int = -1;
    protected var _amPmValue:String = "";
		
    /**
     * @private
     * The UITextFields for the ":" separators.
     */
    protected var separators:Array = [];
		
    /**
     * @private
     * The width of a two-digit number. Used in measurement calculations and
     * positioning.
     */
    private var _numberWidth:Number = 0;
    private var _numberWidthMT:Number = 0;

    private var _numberHeight:Number = 0;
    private var _numberHeightMT:Number = 0;
    /**
     * @private
     * The width of "AM" or "PM". Used in measurement calculations and
     * positioning.
     */
    private var _amPmWidth:Number = 0;
    private var _amPmWidthMT:Number = 0;
		
    private var _amPmHeight:Number = 0;
    private var _amPmHeightMT:Number = 0;
		
    //--------------------------------------
    //  Public Methods
    //--------------------------------------
		
    /**
     * @private
     */
    override public function styleChanged(styleProp:String):void {
      super.styleChanged(styleProp);
			
      if(!styleProp || styleProp.indexOf("font") >= 0 
	 || styleProp.indexOf("text") >= 0
	|| StyleManager.isSizeInvalidatingStyle(styleProp)) {
	layoutStyleChanged = true;
				
	initWidthValues(true);

	this.invalidateSize();
	this.invalidateDisplayList();
      }
    }
		
    //--------------------------------------
    //  Protected Methods
    //--------------------------------------
		
    protected function initWidthValues(forceRecalc:Boolean = false):void {
      if (_numberWidth > 0 && !forceRecalc) {

 	return;
      }

      //we have to do this even if we have explicit dimensions
      //because positioning relies on the size of two characters
      var doubleZero:String = "00";
      var tempTextField:UITextField = new UITextField();
      tempTextField.styleName = this.styleName;
      tempTextField.ignorePadding = true;
      tempTextField.text = doubleZero;
      
      //figured invalidation and validation would be enough
      //but we have to add and remove the UITextField too!
      this.addChild(tempTextField);
				
      _numberWidth = tempTextField.measuredWidth;
      _numberHeight = tempTextField.measuredHeight;
      var doubleZeroMT:TextLineMetrics = measureText(doubleZero);
      _numberWidthMT = doubleZeroMT.width + TEXT_WIDTH_PADDING;
      _numberHeightMT = doubleZeroMT.height;

      this.removeChild(tempTextField);

      var mmText:String = "MM";
      var amPmTextField:UITextField = new UITextField();
      amPmTextField.styleName = this;
      amPmTextField.ignorePadding = true;
      amPmTextField.text = mmText;
				
      //figured invalidation and validation would be enough
      //but we have to add and remove the UITextField too!
      this.addChild(amPmTextField);
				
      _amPmWidth = amPmTextField.measuredWidth;
      _amPmHeight = amPmTextField.measuredHeight;
      var mmTextMT:TextLineMetrics = measureText(mmText);
      _amPmWidthMT = mmTextMT.width + TEXT_WIDTH_PADDING;
      _amPmHeightMT = mmTextMT.height;

      this.removeChild(amPmTextField);

      this.invalidateSize();
      this.invalidateDisplayList();

    }

    /**
     * @private
     */
    override protected function createChildren():void {
      super.createChildren();
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var showAMPM:Boolean = this.getStyle("showAMPM");
			
      var separatorField:UITextField = getSeparator();
      this.addChild(separatorField);
      this.separators.push(separatorField);
      if (showSeconds) {
	separatorField = getSeparator();
	this.addChild(separatorField);
	this.separators.push(separatorField);
      }
			
      // update the input fields
      hoursCombo = createValueComboBox();
      hoursCombo.dataProvider = getHoursList();
      hoursCombo.toolTip = this.getStyle("hoursText");
      hoursCombo.restrict = "0-9";
      hoursCombo.addEventListener(ListEvent.CHANGE, setDefaultTimeValues);
      hoursCombo.addEventListener(ListEvent.CHANGE,  timeComboChangeListener);
      hoursCombo.labelFunction = getNumberValue;

      minutesCombo = createValueComboBox();
      minutesCombo.dataProvider = getMinutesList();
      minutesCombo.toolTip = this.getStyle("minutesText");
      minutesCombo.restrict = "0-9";
      minutesCombo.addEventListener(ListEvent.CHANGE,  timeComboChangeListener);
      minutesCombo.labelFunction = getNumberValue;
			
      if (showSeconds) {
	secondsCombo = createValueComboBox();
	secondsCombo.dataProvider = getSecondsList();
	secondsCombo.toolTip = this.getStyle("secondsText");
	secondsCombo.restrict = "0-9";
	secondsCombo.addEventListener(ListEvent.CHANGE,  timeComboChangeListener);
	secondsCombo.labelFunction = getNumberValue;
      }
			
      if (useTwelveHourFormat && showAMPM) {
	AMPMCombo = createValueComboBox();
	AMPMCombo.dataProvider = getAmPmList();
	var amText:String = getStyle("AMText");
	var pmText:String = getStyle("PMText");
	var amPmToolTip:String = amText + "/" + pmText;
	AMPMCombo.toolTip = amPmToolTip;
	AMPMCombo.addEventListener(KeyboardEvent.KEY_DOWN, AMPMKeyDownHandler);
	AMPMCombo.addEventListener(ListEvent.CHANGE,  timeComboChangeListener);
      }

    }
		
    protected function createValueComboBox():ComboBox {
      var comboBox:ComboBox = createComboBox();

      return comboBox;

    }

    protected function getSeparator():UITextField {
      var separatorField:UITextField = new UITextField();
      separatorField.styleName = this;
      separatorField.ignorePadding = true;
      separatorField.selectable = false;
      separatorField.enabled = this.enabled;
      separatorField.text = ":";

      return separatorField;
    }


    /**
     * @private
     */
    override protected function commitProperties():void
    {
      if(this.enabledChanged)
	{
	  for each(var separator:UITextField in this.separators)
		    {
		      separator.enabled = this.enabled;
		    } 
	}
			
      super.commitProperties();
			
      setComboValues();

    }
		
    /**
     * @private
     */
    override protected function measure():void {
      super.measure();
      if (timeComboWidth && !layoutStyleChanged) {

	measuredMinWidth = timeComboWidth;
	measuredMinHeight = timeComboHeight;
	measuredWidth = timeComboWidth;
	measuredHeight = timeComboHeight;
	return;
      }

      layoutStyleChanged = false;
      initWidthValues();

      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var showAMPM:Boolean = this.getStyle("showAMPM");
			
      if (this.border) {
	var metrics:EdgeMetrics = (this.border is IBorder 
				   ? IBorder(this.border).borderMetrics
				   : EdgeMetrics.EMPTY);
	this.measuredWidth = metrics.left + metrics.right;
	this.measuredHeight = metrics.top + metrics.bottom;
      }
			
      var compWidths:Number = getComponentWidths();
      if (compWidths > 0) {
	this.measuredWidth += compWidths;
      }
       this.measuredWidth += timeComboWidthPad;

       this.measuredHeight += getComboHeight();
       this.measuredHeight += comboHeightPad;
//        this.percentHeight = 100;

      //since we don't want the text being cut off, the min width and
      //height are the same as the regular values.
      this.measuredMinWidth = this.measuredWidth;
      this.measuredMinHeight = this.measuredHeight;
      
      timeComboWidth = measuredWidth;
      timeComboHeight = measuredHeight;
    }

    /**
     * @private
     * Overrides default layout algorithm to include the separator fields. 
     */
    override protected function layoutChildren():void {

      var showSeconds:Boolean = this.getStyle("showSeconds");
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showAMPM:Boolean = this.getStyle("showAMPM");

      var metrics:EdgeMetrics = (this.border && this.border is IBorder
				 ? IBorder(this.border).borderMetrics
				 : EdgeMetrics.EMPTY);

      var xPosition:Number = metrics.left;
      var yPosition:Number = metrics.top;
      if (!showSeconds && (!useTwelveHourFormat || !showAMPM)) {
	comboPercentWidth = 45;
      }
      else if (!showSeconds) {
	comboPercentWidth = 32;
      }
      else if (!useTwelveHourFormat || !showAMPM) {
	comboPercentWidth = 32;
      }
			
      hoursCombo.x = xPosition;
      hoursCombo.y = yPosition;
      var hcWidth:Number = getComboWidth();
      hoursCombo.minWidth = hcWidth;
      hoursCombo.minHeight = getComboHeight();
      hoursCombo.setActualSize(hcWidth, getComboHeight());

      xPosition += hoursCombo.width;
			
      var separatorField:UITextField = UITextField(this.separators[0]);
      separatorField.x = xPosition;
      separatorField.y = yPosition;
      var sepWidth:Number = Math.min(Math.max(0, 
					      unscaledWidth - metrics.right
					      - separatorField.x), 
				     separatorField.measuredWidth);

      separatorField.setActualSize(sepWidth, getComboHeight());
      xPosition += separatorField.measuredWidth;
			
      minutesCombo.x = xPosition;
      minutesCombo.y = yPosition;
      var mcWidth:Number = getComboWidth(); // this._numberWidth;
      minutesCombo.minWidth = mcWidth;
      minutesCombo.minHeight = getComboHeight();
       minutesCombo.setActualSize(mcWidth, getComboHeight());

      xPosition += minutesCombo.width;

      separatorField = UITextField(this.separators[1]);
      if (null != separatorField) {
	sepWidth = Math.min(Math.max(0, unscaledWidth - metrics.right
				     - separatorField.x), 
			    separatorField.measuredWidth);
	separatorField.x = xPosition;
	separatorField.y = yPosition;

	separatorField.setActualSize(sepWidth, getComboHeight());
	xPosition += separatorField.measuredWidth;
      }
			
      if (showSeconds) {
	secondsCombo.x = xPosition;
	secondsCombo.y = yPosition;
	var scWidth:Number = getComboWidth(); // this._numberWidth;
 	secondsCombo.setActualSize(scWidth, getComboHeight());
	secondsCombo.minWidth = scWidth;
	secondsCombo.minHeight = getComboHeight();

	xPosition += secondsCombo.width;
      }

      if (useTwelveHourFormat && showAMPM) {
	AMPMCombo.x = xPosition + amPmGap;
	AMPMCombo.y = yPosition;
	var acWidth:Number = AMPMCombo.measuredWidth;
 	AMPMCombo.setActualSize(acWidth, getComboHeight());
	AMPMCombo.minWidth = acWidth;
	AMPMCombo.minHeight = getComboHeight();
      }

    }

    public function getComponentWidths():Number {
      var ttlWidth:Number = 0;

      var showSeconds:Boolean = this.getStyle("showSeconds");
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showAMPM:Boolean = this.getStyle("showAMPM");

      var compWidth:Number = 0;
			
      compWidth = getComboWidth(); // hoursCombo.width;
      ttlWidth += compWidth;


      var separatorField:UITextField = UITextField(this.separators[0]);
      compWidth = separatorField.width;
      ttlWidth += compWidth;
			
      compWidth = getComboWidth(); // minutesCombo.width;
      ttlWidth += compWidth;

      separatorField = UITextField(this.separators[1]);
      if (null != separatorField) {
	compWidth = separatorField.width;

	ttlWidth += compWidth;
      }
			
      if (showSeconds) {

	compWidth = getComboWidth(); // secondsCombo.width;

	ttlWidth += compWidth;
      }

      if (useTwelveHourFormat && showAMPM) {

	compWidth = (AMPMCombo.width > 0 ? AMPMCombo.width : getComboWidth());
	compWidth += amPmGap;
	ttlWidth += compWidth;
      }

      ttlWidth += timeComboWidthPad;

      return ttlWidth;
    }
	
    protected function getComponentWidthsOrig():Number {
      var allInitialized:Boolean = true;
      componentWidths = new ArrayCollection();

      var showSeconds:Boolean = this.getStyle("showSeconds");
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showAMPM:Boolean = this.getStyle("showAMPM");

      var compWidth:Number = 0;
			
      compWidth = getComboWidth(); // hoursCombo.width;
      componentWidths.addItem(compWidth);

      var separatorField:UITextField = UITextField(this.separators[0]);
      compWidth = separatorField.width;
      componentWidths.addItem(compWidth);
			
      compWidth = getComboWidth(); // minutesCombo.width;
      componentWidths.addItem(compWidth);

      separatorField = UITextField(this.separators[1]);
      if (null != separatorField) {
	compWidth = separatorField.width;
	componentWidths.addItem(compWidth);
      }
			
      if (showSeconds) {

	compWidth = getComboWidth(); // secondsCombo.width;
	componentWidths.addItem(compWidth);
      }

      if (useTwelveHourFormat && showAMPM) {

	compWidth = (AMPMCombo.width > 0 ? AMPMCombo.width : getComboWidth());
	compWidth += amPmGap;
	componentWidths.addItem(compWidth);
      }

      var ttlWidth:Number = 0;
      for (var idx:int = 0; idx < componentWidths.length; idx++) {
	compWidth = componentWidths.getItemAt(idx) as Number;
	if (compWidth == 0) {
	  allInitialized = false;
	}

	ttlWidth += compWidth;
      }

      ttlWidth += timeComboWidthPad;

      if (!allInitialized) {
	ttlWidth = -1;
      }
      return ttlWidth;
    }
	
    override public function getComboHeight():Number {
      var currHeight:Number;
      if (_numberHeight > comboHeight) {
	currHeight = _numberHeight;
      }
      else {
	currHeight = comboHeight;
      }

      return currHeight;
    }

	
    //--------------------------------------
    //  Protected Event Handlers
    //--------------------------------------
		
    protected function setDefaultTimeValues(event:ListEvent):void {
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var defaultMorning:Boolean = this.getStyle("defaultMorning");
      var showAMPM:Boolean = this.getStyle("showAMPM");

      if (minutesCombo.selectedIndex < 0) {
	var minute:Number = 0;
	minutesCombo.selectedItem = minute;


      }
      if (showSeconds && secondsCombo.selectedIndex < 0) {
	var second:Number = 0;
	secondsCombo.selectedItem = second;
      }

      if (useTwelveHourFormat && showAMPM && AMPMCombo.selectedIndex < 0) {

	var timeOfDayText:String = getStyle("AMText");
	if (!defaultMorning) {
	  timeOfDayText = getStyle("PMText");
	}
	AMPMCombo.selectedItem = timeOfDayText;
      }

    }

    protected function timeComboChangeListener(event:ListEvent):void {
	this.dispatchEvent(new Event(Event.CHANGE));
    }

    /**
     * @private
     *
     * Validates input in the AMPM field. Also ensures that the text stays
     * selected.
     */
    protected function AMPMKeyDownHandler(event:KeyboardEvent):void {
      if (null != _timeValue) {
	var value:Date = new Date(this.timeValue.valueOf());
	var hours:Number = value.getHours();
	if(event.charCode == String("a").charCodeAt(0) && hours >= 12)
	  {
	    value.setHours(hours - 12);
	  }
	else if(event.charCode == String("p").charCodeAt(0) && hours < 12)
	  {
	    value.setHours(hours + 12);
	  }
	else if(event.keyCode == Keyboard.UP || event.keyCode == Keyboard.DOWN)
	  {
	    //if the user presses up or down, make sure the focus stays in place.
	    var comboBox:ComboBox = ComboBox(event.target);
	    // 	  this.callLater(comboBox.setSelection, [0, comboBox.length]);
	  }
	this.timeValue = value;
	this.dispatchEvent(new Event(Event.CHANGE));
      }
    }
		
   protected function getHoursList():ArrayCollection {
      if (null == hoursList) {

	var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
	var numStart:int = 0;
	var numLimit:int = hoursPerDay;
	if (useTwelveHourFormat) {
	  numStart = 1;
	  numLimit = hoursPerHalfDay;
	}
	hoursList = getNumberList(numLimit, hoursInc, numStart);
      }
      return hoursList;
    }

    protected function getMinutesList():ArrayCollection {
      if (null == minutesList) {

	minutesInc = this.getStyle("minutesIncrement");
	minutesList = getNumberList(minutesPerHour - 1, minutesInc);
      }
      return minutesList;
    }

    protected function getSecondsList():ArrayCollection {
      if (null == secondsList) {

	secondsInc = this.getStyle("secondsIncrement");
	secondsList = getNumberList(secondsPerMinute - 1, secondsInc);
      }
      return secondsList;
    }

    protected function getAmPmList():ArrayCollection {
      if (null == amPmList) {
	amPmList = new ArrayCollection();


	var amText:String = getStyle("AMText");
	var pmText:String = getStyle("PMText");

	amPmList.addItem(amText);


	amPmList.addItem(pmText);

      }
      return amPmList;
    }

    protected function getComboValues():Date {
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var showAMPM:Boolean = this.getStyle("showAMPM");

      _timeValue = null;

      if (null != hoursCombo && hoursCombo.selectedIndex >= 0) {

	_hour = hoursCombo.selectedItem as Number;

	if (useTwelveHourFormat && showAMPM) {
	  _amPmValue = AMPMCombo.selectedItem as String;
	  if (_amPmValue == "PM") {
	    _hour += 12;
	  }
	}
      }

      if (null != minutesCombo && minutesCombo.selectedIndex >= 0) {

	_minute = minutesCombo.selectedItem as Number;
      }

      if (showSeconds && null != secondsCombo
	  && secondsCombo.selectedIndex >= 0) {
	_second = secondsCombo.selectedItem as Number;
      }

      if (_hour >= 0 && _minute >= 0) {
	if (_second < 0) {
	  _second = 0;
	}
	_timeValue = new Date();
	_timeValue.setHours(_hour);
	_timeValue.setMinutes(_minute);
	_timeValue.setSeconds(_second);
      }

      return _timeValue;
    }


    protected function setComboValues():void {
      var useTwelveHourFormat:Boolean = this.getStyle("useTwelveHourFormat");
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var showAMPM:Boolean = this.getStyle("showAMPM");
      _hour = -1;
      _minute = -1;
      _second = -1;
      _amPmValue = "";

      if (null == _timeValue) {
	clearComboValues();
	return;
      }

      if (null != hoursCombo) {
	_hour = _timeValue.getHours();
	if (_hour > 12 && useTwelveHourFormat && showAMPM) {

	  _amPmValue = "PM";

	  _hour -= 12;
	}
	else {
	  _amPmValue = "AM";
	}

	if (useTwelveHourFormat && showAMPM) {
	  AMPMCombo.selectedItem = _amPmValue;
	}
	hoursCombo.selectedItem = _hour;
      }

      if (null != minutesCombo) {
	_minute = _timeValue.getMinutes();
	minutesCombo.selectedItem = _minute;
      }


      if (null != secondsCombo) {
	_second = _timeValue.getSeconds();

	secondsCombo.selectedItem = _second;
      }

    }

    protected function clearComboValues():void {
      var showSeconds:Boolean = this.getStyle("showSeconds");
      var showAMPM:Boolean = this.getStyle("showAMPM");

      if (null != hoursCombo) {
	hoursCombo.selectedIndex = -1;
      }

      if (null != minutesCombo) {
	minutesCombo.selectedIndex = -1;
      }
      if (showSeconds && null != secondsCombo) {
	secondsCombo.selectedIndex = -1;
      }

      if (null != AMPMCombo) {
	AMPMCombo.selectedIndex = -1;
      }
      // 	dumpComboValues("clearComboValues");
    }

    protected function dumpComboValues(funcName:String, 
				    hour:Number = -1, 
				    minute:Number = -1, 
				    second:Number = -1):void {

      trace("Caller = " + funcName
			  + ":" + this.name + ":" + this.id);

      trace("Hour = " + hour
			  + " Minute = " + minute 
			  + " Second = " + second);


      trace("Hrs.selIdx = "
			  + (null != hoursCombo 
			     ? hoursCombo.selectedIndex
			     + " HR:" + hoursCombo.selectedItem : "NULL"));
      trace("Mins.selIdx = "
			  + (null != minutesCombo
			     ? minutesCombo.selectedIndex 
			     + " MIN:" + minutesCombo.selectedItem : "NULL"));
      trace("timeValue = " + (_timeValue != null 
			      ? _timeValue.toString() : "NULL"));

    }

    public function getComboWidth():Number {
      comboWidth = 0;
      if (_numberWidth == 0) {
	initWidthValues();
      }
      if (downArrowWidth == 0) {
	downArrowWidth = hoursCombo.getStyle("arrowButtonWidth");
      }
      comboWidth = _numberWidth + downArrowWidth + comboWidthPad;
      if (comboWidth < minComboWidth) {
	comboWidth = minComboWidth;
      }
      return comboWidth;

    }


    [Bindable("valueCommit")]
      public function get timeValue():Date {
      _timeValue = getComboValues();
      return _timeValue;
    }

    public function set timeValue(timeValue:Date):void {
      _timeValue = timeValue;
      setComboValues();

      this.invalidateProperties();
      this.dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
    }

    public function get hour():int {
      return _hour;
    }


     protected function set hour(hour:int):void {
       _hour = hour;
     }


    public function get minute():int {
      return _minute;
    }

    protected function set minute(minute:int):void {
       _minute = minute;
     }

    public function get second():int {
      return _second;
    }

     protected function set second(second:int):void {
       _second = second;
     }

    public function get amPmValue():String {
      return _amPmValue;
    }

  }
}

